/**
 * 总体思路
 * 1 棋盘：给定一个矩阵。矩阵再+1则为边界
 * 2 互动方式：选定一对位置，进行判断
 *  1 因为是互联的，所以从1 - 2和从2 - 1的路径是可逆的。于是默认 1 - 2 即可
 *  2 判断包围性
 *    当且仅当两者相邻时（abs(delta(x)) === 1, abs(delta(y) === 1， 且仅有一者可以为1），允许二者周围（4方向）全包围。否则任一选项被全包围即不可使用
 *  3 判断通路
 *    连连看通路具体允许2折即方向90°改变2次。具体扫描方式：从某一方向出发，直线前进，走到不能走（边界/非选中对象）转向，再次重复。如果两次转向没有到达，则不可达。（dfs寻路？）
 */

const diffrentTypes = 25 // 这样有25个格子

export const block = (type: number, row: number = -1, col: number = -1) => ({
  type,
  row,
  col,
})

const getRandInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min

const swapBlock = (a: any ,b: any) => {
  [a.type, b.type] = [b.type, a.type]
}

const shuffleBlks = (board: any[][], dir: number) => {
  // dir 1 - row 2 - col
  if(dir === 1) {
    for(let i = 1;i < 6;i += 1){
      for(let j = 11 - 1;j >= 1;j -= 1){
        const next = getRandInt(1, j);
        const currTarg = board[i][j]
        swapBlock(board[i][next], currTarg)
      }
    }
  } else {
    for(let i = 1;i < 11; i += 1){
      for(let j = 6 -1; j >= 1; j -= 1) {
        const next = getRandInt(1, j);
        const currTarg = board[j][i]
        swapBlock(board[next][i], currTarg)

      }
    }
  }
}

export const createBoard = () => {
  const blocks = [] as any[]
  for (let type = 0; type < diffrentTypes; type += 1) {
    // 怎么走？
    blocks.push(block(type))
    blocks.push(block(type))
  }
  // 布置board -> 5 * 10 => 7 * 12
  const board = new Array(7).fill(0).map(() => new Array(12).fill(0))
  // 1 - 10 布置block
  // 1 - 5 布置block
  board.forEach((row: any[], i) => {
    if (i === 0 || i === 6) {
      return row.forEach((_e, j) => {
        row[j] = block(-1)
      })
    }
    row.forEach((_col, j) => {
      if (j === 0 || j === 11) {
        row[j] = block(-1, j, i)
      } else {
        const item = blocks[i * 10 + j - 11]
        item.row = i
        item.col = j
        row[j] = item
      }
    })
  })

  // 栈混洗
  shuffleBlks(board, 1)
  shuffleBlks(board, 2)

  return board
}

const calcTurns = (arr: any[][]) => {
  let turns = 0
  const windowSize = 3
  let start = 0
  while (start + windowSize <= arr.length) {
    const st = arr[start]
    const ed = arr[start + windowSize - 1]
    if (Math.abs(st[0] - ed[0]) === 1 && Math.abs(st[1] - ed[1]) === 1) {
      turns += 1
    }
    start += 1
  }
  return turns
}

const asEqual = ([r1, c1] : number[], [r2, c2]: number[]) => {
  return r1 === r2 && c1 === c2
}

const calcDup = (arr: any) => {
  // 只看n和n-2是否相同即可！
  const n = arr.length

  return asEqual(arr[n-1], arr[n-3])
}

const createNextMove = (r1: number, c1: number, r2: number, c2: number) => {
  const movement = [
    [r1, c1 + 1], // 向右
    [r1 + 1, c1], // 向下
    [r1, c1 - 1], // 向左
    [r1 - 1, c1], // 向上
  ]
  
  // 计算向量方向
  const dr = r2 - r1
  const dc = c2 - c1

  if (dr <= 0 && dc > 0) {
    return movement
  }
  if (dr < 0 && dc <= 0) {
    return [...movement.slice(1), movement[0]]
  }
  if (dr >= 0 && dc < 0) {
    return [...movement.slice(2), ...movement.slice(0, 2)]
  }
  return [movement[3], ...movement.slice(0, 3)]
}

export const findPath = ([r1, c1]: number[], [r2, c2]: number[], _board: any[][], visitPath: any[]) : boolean => {
  let isOk = true
  
  if (r1 < 0 || c1 < 0 || r1 > 6 || c1 > 11) { // 边缘判断
    isOk = false
    return isOk
  }
  visitPath.push([r1, c1])
  console.log('step into', `${r1}, ${c1}`)

  if (visitPath.length > 3) { // 当前路径已访问节点 防止 1 - 2 -1 的情况
    if (calcDup(visitPath)) {
      isOk = false
      console.log('boom!', visitPath.map(e => e.join(',')).join('->'))
      return isOk
    }
  }

  if (visitPath.length >= 5) { // 路径长度超过5 才有可能出现3个弯 -> 避免成环
    const turns = calcTurns(visitPath)
    if (turns > 2) {
      isOk = false
      console.log('over turns at', `${r1}, ${c1}`, turns)
      return isOk
    }
  }

  if (asEqual([r1, c1], [r2, c2])) { // 到终点
    console.log('as ok!')
    return isOk
  } else if (_board[r1][c1].type > -1 && visitPath.length > 1) { // 没到终点，又有值，不能走了
    console.log(`not a passible move for ${_board[r1][c1].type} at ${r1}:${c1}`)
    isOk = false
    visitPath.pop()
    return isOk
  }

  console.log('start!', `${r1}, ${c1} -> ${r2}, ${c2}`, visitPath.length)

  const startLength = visitPath.length

  const pastMove = visitPath[startLength - 2] || [-1, -1]

  const nextMoves = createNextMove(r1, c1, r2, c2)
  for (let nextMove of nextMoves) {
    let pathRes = asEqual(nextMove, pastMove) ? false :findPath(nextMove, [r2, c2], _board, visitPath)
    if (pathRes) {
      return pathRes
    } else {
      visitPath.length = startLength
    }
  }

  // 四个方向全部gg
  return false
}